package utils

import (
	"crypto/md5"
	"crypto/rand"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/oliveagle/jsonpath"
	"io/fs"
	"io/ioutil"
	"log"
	"math"
	"net"
	"os"
	"path"
	"regexp"
	"runtime"
	"strings"
	"time"
)

var Version = 1.23

func GetHostName() string {
	name, err := os.Hostname()
	if err != nil {
		return ""
	}
	return name
}

func Now() string {
	return time.Now().Format("2006-01-02--15:04:05")
}

func TcpPortIsOpen(ports []string) (bool, string) {
	host := "127.0.0.1"
	for _, port := range ports {
		timeout := time.Second
		conn, err := net.DialTimeout("tcp", net.JoinHostPort(host, port), timeout)
		if err != nil {
			return false, fmt.Sprintf("Connecting tcp %s error:%s", port, err.Error())
		}
		if conn != nil {
			defer conn.Close()
			//fmt.Println("Opened", net.JoinHostPort(host, port))
		}
	}
	return true, "ok"
}

// 验证结果 有问题
//func UdpPortIsOpen( ports []string) (bool,string){
//	host := "172.25.129.166"
//	for _, port := range ports {
//		timeout := time.Second*3
//		conn, err := net.DialTimeout("udp", net.JoinHostPort(host, port), timeout)
//		if err != nil {
//			return false, fmt.Sprintf("Connecting udp %s error:%s", port,err.Error())
//		}
//		if conn != nil {
//			defer conn.Close()
//			fmt.Println("Opened", net.JoinHostPort(host, port))
//		}
//	}
//	return true,"ok"
//}

func DefaultUpdateRpmCommands(rpmName, downServer, md5str, servername, confdata string) (res []Command, err error) {
	res = append(res, Command{CommandName: "DownFile", CommandArg: downServer + rpmName + "," + md5str})
	res = append(res, Command{CommandName: "RpmUpdate", CommandArg: "./download/" + strings.TrimSpace(rpmName)})
	if confdata != "" {
		res = append(res, Command{CommandName: "WriteConf", CommandArg: confdata})
	}
	res = append(res, Command{CommandName: "ServerRestart", CommandArg: servername})
	return
}

func ForceInstallRpmCommands(rpmName, downServer, md5str, servername, confdata string) (res []Command, err error) {
	res = append(res, Command{CommandName: "DownFile", CommandArg: downServer + rpmName + "," + md5str})
	baseRpm := GetBaseRpm(rpmName)
	res = append(res, Command{CommandName: "RpmUninstall", CommandArg: baseRpm})
	res = append(res, Command{CommandName: "RpmInstall", CommandArg: "./download/" + strings.TrimSpace(rpmName)})
	if confdata != "" {
		res = append(res, Command{CommandName: "WriteConf", CommandArg: confdata})
	}
	res = append(res, Command{CommandName: "ServerRestart", CommandArg: servername})
	return
}

type Rpms []Rpm
type Rpm struct {
	Rpm string
	Md5 string
}

func UpdateConfRestartCommands(cs *Commands, dataServer Data) {
	args := strings.Join(dataServer.ServerName, " ")
	stop := SF.GetFunc("ServerStop").(Command)
	stop.CommandArg = args
	start := SF.GetFunc("ServerStart").(Command)
	start.CommandArg = args
	*cs = append(*cs, stop)
	*cs = append(*cs, SF.GetFunc("WriteConf").(Command))
	*cs = append(*cs, start)
}

func UpdateConfCommands(cs *Commands) {
	*cs = append(*cs, SF.GetFunc("WriteConf").(Command))
}

func ConvertJobSliceToCommands(cmds []string, cs *Commands, downServer FileServerConf, rpms Rpms, dataServer Data) error {
	for _, cmd := range cmds {
		args := ""
		cmd = strings.Replace(cmd, " ", "", -1)

		// 排除异常部署命令
		cmdFunc := SF.GetFunc(cmd)
		if cmd == "WriteConf" {
			goto AddCmd
		}
		if cmdFunc == nil {
			return errors.New(fmt.Sprintf("转换部署命令[%s]错误", cmd))
		}

		if cmd == "ServerStart" || cmd == "ServerRestart" || cmd == "ServerStop" {
			args = strings.Join(dataServer.ServerName, " ")
			goto AddCmd
		}
		if cmd == "PortCheck" {
			args = dataServer.PortCheck
			goto AddCmd
		}
		if cmd == "DownFile" {
			// 查找相应下载文件的md5值并加入参数中
			for _, rpm := range rpms {
				args = downServer.Rpm1 + rpm.Rpm + "," + downServer.Rpm2 + rpm.Rpm + "," + rpm.Md5
				c := cmdFunc.(Command)
				c.CommandArg = args
				*cs = append(*cs, c)
			}
			continue
		}
		if cmd == "RpmUpdate" || cmd == "RpmInstall" {
			for _, rpm := range rpms {
				args = "./download/" + strings.TrimSpace(rpm.Rpm)
				c := cmdFunc.(Command)
				c.CommandArg = args
				*cs = append(*cs, c)
			}
			continue
		}
		if cmd == "RpmUninstall" {
			//for _,rpm := range rpms {
			//	args = GetBaseRpm(rpm.Rpm)
			//	c := cmdFunc.(Command)
			//	c.CommandArg = args
			//	*cs = append(*cs, c)
			//}
			for _, rpmNmae := range dataServer.RpmName {
				c := cmdFunc.(Command)
				c.CommandArg = rpmNmae
				*cs = append(*cs, c)
			}
			continue
		}
	AddCmd:
		c := cmdFunc.(Command)
		c.CommandArg = args
		log.Println("args:", args, "command:", c)
		*cs = append(*cs, c)
	}
	return nil
}

// 把job model表里的content字段转化为任务
func ConvertJobContentToCommands(content string, cs *Commands, downServer, md5str string) error {
	defer func() {
		if err := recover(); err != nil {
			log.Println("recover捕获错误：", err)
		}
	}()
	for _, c := range strings.Split(content, "\n") {
		c = strings.Replace(c, " ", "", -1)
		if len(c) == 0 {
			continue
		}
		aCmd := strings.Split(c, "|")
		if len(aCmd) == 2 {
			if len(aCmd[0]) == 0 || len(aCmd[1]) == 0 {
				panic(fmt.Sprintf("数据内容有误：不能为空"))
			}
			cmd, args := aCmd[0], aCmd[1]
			if cmd == "DownFile" {
				// 查找相应下载文件的md5值并加入参数中
				args = downServer + args + "," + md5str
			}
			if cmd == "RpmUpdate" || cmd == "RpmInstall" {
				args = "./download/" + strings.TrimSpace(args)
			}
			*cs = append(*cs, Command{CommandName: cmd, CommandArg: args})
		} else {
			panic(fmt.Sprintf("数据格式有误：%s", c))
		}
	}
	return nil
}

// []utils.Command 转为str,用于jobModel.context 和jobstatus 的JobDefinition
func ConvertToStr(c []Command) string {
	bc, err := json.Marshal(c)
	if err != nil {
		return ""
	}
	return string(bc)
}

// str 转为[]utils.Command，用于jobModel.context 和jobstatus 的JobDefinition
func ConvertToCmd(s string) []Command {
	result := []Command{}
	err := json.Unmarshal([]byte(s), &result)
	if err != nil {
		return nil
	}
	return result
}

// 检测文件是否存在
func FileExist(path string) bool {
	_, err := os.Lstat(path)
	return !os.IsNotExist(err)
}

func DirExist(path string) bool {
	_, err := os.Lstat(path)
	return !os.IsNotExist(err)
}

// 计算md5
func Md5sum(filepath string) (string, error) {
	f, err := os.Open(filepath)
	if err != nil {
		str1 := "Open err"
		return str1, err
	}
	defer f.Close()

	body, err := ioutil.ReadAll(f)
	if err != nil {
		str2 := "ioutil.ReadAll"
		return str2, err
	}
	md5 := fmt.Sprintf("%x", md5.Sum(body))
	runtime.GC()
	return md5, nil
}

//
func SliceToStr(s1 []string, jobPolicy int) string {
	result := ""
	if jobPolicy == 0 {
		result = "0:" + strings.Join(s1, ",") + ";"
		return result
	}
	// 计算group数目
	maxGroup := len(s1) / jobPolicy
	if len(s1)%jobPolicy != 0 {
		maxGroup = maxGroup + 1
	}
	// 分组切开
	for i := 1; i <= maxGroup; i++ {
		groupClients := []string{}
		left := (i - 1) * jobPolicy
		if left < 0 {
			left = 0
		}
		right := i * jobPolicy
		log.Printf("left:%d right:%d\n", left, right)
		if i*jobPolicy >= len(s1) {
			groupClients = s1[left:len(s1)]
			log.Println("A----", groupClients)

		} else {
			groupClients = s1[left:right]
			log.Println("b----", groupClients)
		}
		result = result + fmt.Sprintf("%d:", i) + strings.Join(groupClients, ",") + ";"
		log.Println(result)
	}
	return result
}

// 判断是否存在
func isExist(path string) bool {
	_, err := os.Stat(path)
	if err != nil {
		if os.IsExist(err) {
			return true
		}
		if os.IsNotExist(err) {
			return false
		}
		fmt.Println(err)
		return false
	}
	return true
}

func StrToSlice(str string) []string {
	result := []string{}
	for _, groups := range strings.Split(str, ";") {
		g := strings.Split(groups, ":")
		if len(g) == 2 {
			lg := strings.Split(g[1], ",")
			result = append(result, lg...)
		}

	}
	return result
}

func GetAgentVersion() float64 {
	return Version
}

func GetNetHadr() net.HardwareAddr {
	nets, err := net.Interfaces()
	if err != nil {
		panic("Error:" + err.Error())
	}
	for _, inter := range nets {
		mac := inter.HardwareAddr
		if mac != nil {
			return mac
		}
	}
	return nil
}

// 获取文件的时间
func GetFileTime(filename string) time.Time {
	f, _ := os.Stat(filename)
	return f.ModTime()
}

// 获取包含这个时间的日志超这个时间，并且最接近的一个文件名
func GetFirstFileViaTime(dir, filename string, t int64) fs.FileInfo {
	files, _ := ioutil.ReadDir(dir)
	var firtFile fs.FileInfo
	for _, f := range files {
		//// 如果文件名 不包含这些字符串就跳过
		//if !strings.Contains(f.Name(),filename){
		//	log.Println(f.Name(),"继续")
		//	continue
		//
		//}
		//log.Println("包含")
		if len(f.Name()) >= len(filename) && filename == f.Name()[:len(filename)] {
			fi, err := os.Stat(path.Join(dir, f.Name()))
			if err != nil {
				log.Println(f.Name(), "err:", err.Error())
				fmt.Println(err)
			}
			currTime := fi.ModTime().Unix()
			if currTime > t {
				// 第一个大于指定时间的文件
				if firtFile == nil {
					firtFile = fi
				} else if fi.ModTime().Before(firtFile.ModTime()) {
					firtFile = fi
				}
			}

		}
		log.Println("文件头不一样 路过", f.Name())

	}
	return firtFile
}

// 获取里所有的文件信息
func GetFileList(dir string) []fs.FileInfo {
	files, _ := ioutil.ReadDir(dir)
	return files
}

// 取得rpm包名
func GetBaseRpm(rpm string) string {
	re := regexp.MustCompile(`(^.*?)-\d`)
	match := re.FindStringSubmatch(rpm)
	return match[1]
}

// 删除元素
func DelItem(vs []string, s string) []string {
	maxIdx := len(vs) - 1
	for i := maxIdx; i >= 0; i-- {
		if s == vs[i] {
			vs = append(vs[:i], vs[i+1:]...)
		}
	}
	return vs
}

func RandomAddr() string {
	buf := make([]byte, 6)
	_, err := rand.Read(buf)
	if err != nil {
		fmt.Println("error:", err)
		return ""
	}
	// Set the local bit
	buf[0] |= 2
	return (fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]))
}

func GetConfType() int {
	var mcuconf = "/etc/craft/relayserver/relayserver.conf"
	r, _ := ioutil.ReadFile(mcuconf)

	var json_data interface{}
	if err := json.Unmarshal(r, &json_data); err != nil {
		return 999
	}

	rt, _ := jsonpath.JsonPathLookup(json_data, "$.LocalMachine.resourceType2")
	if rt == nil {
		return 0
	}
	fmt.Print(rt)
	frt := rt.(float64)
	return int(math.Ceil(frt))
}
